﻿using System.Collections.Generic;
using System.Xml.Linq;
using TTEngine.Graphics;
using Silk.NET.OpenGLES;
using System;
namespace TTEngine.Resource
{

    public enum ResourceType
    {
        Unknown,
        ShaderProgram,
        ShaderObj,
        Texture,
        Material,
        Mesh,
        Animation,
        Audio,
    }
    public interface IRefObj
    {
        public int UseCount
        {
            get;
        }
        public bool CanDispose
        {
            get;
        }
        public bool HasDispose
        {
            get;
        }
        public void AddRef();
        public void DecRef();
    }
    public abstract class ResourceObj : IResource
    {
        protected ResourceObj(ResourcePackage package)
        {
            this.Package = package;
            this.ResourceID = ResourcePackage.GenID();
            package.AddResource(this);
        }
        public ResourcePackage Package
        {
            get;
            private set;
        }
        public void SetUniqueName(string name)
        {
            if (Package.GetNamedResource(this.GetType(), name) != null)
                throw new Exception("有同名资源，无法使用这个名字");

            if (this.UniqueName != null)
                Package.RemoveUniqueName(this.UniqueName, this);

            Package.SetUniqueName(name, this);
            this.UniqueName = name;

        }
        protected abstract void OnDestory();
        public void Destory()
        {
            if (!this.CanDispose)
                throw new Exception("引用计数不为0，还不能销毁");
            if (this.HasDispose)
                throw new Exception("已经销毁");

            OnDestory();

            this.HasDispose = true;
            this.CanDispose = false;
        }
        public int UseCount
        {
            get;
            private set;
        }

        public bool CanDispose
        {
            get;
            private set;
        }

        public bool HasDispose
        {
            get;
            protected set;
        }

        public string UniqueName
        {
            get; private set;
        }

        public uint ResourceID
        {
            get; private set;
        }

        public void AddRef()
        {
            if (HasDispose)
                throw new Exception("res has disposed");
            UseCount++;
        }


        public bool TryDecRefAndDestory()
        {
            if (HasDispose)
                return false;
            UseCount--;
            if (UseCount <= 0)
            {
                CanDispose = true;
                Destory();
                return true;
            }
            return false;
        }
        public void DecRef()
        {
            if (HasDispose)
                throw new Exception("res has disposed");
            UseCount--;
            if (UseCount <= 0)
                CanDispose = true;
        }


    }

    public abstract class RefObj : IRefObj
    {
        public int UseCount
        {
            get;
            private set;
        }

        public bool CanDispose
        {
            get;
            private set;
        }

        public bool HasDispose
        {
            get;
            protected set;
        }

        public void AddRef()
        {
            UseCount++;
        }

        public void DecRef()
        {
            UseCount--;
            if (UseCount <= 0)
                CanDispose = true;
        }
    }

    //资源代表无法被GC回收的系统资源，主要是GL相关的
    public interface IResource : IRefObj
    {

        string UniqueName
        {
            get;
        }
        //设置唯一的名字，这会被放进资源池中，以后可以用这个名字直接找    
        public void SetUniqueName(string name);
        uint ResourceID
        {
            get;
        }

        //资源释放接口在注册时一起提供，以进行封闭
        void Destory();
    }


    //NamedResource 是可以用Name唯一定位的资源

    public class NamedResourceMap : Dictionary<string, uint>
    {


    }
    public static class ResourceAll
    {
        public static T GetNamedResource<T>(string name) where T : class, IResource
        {
            var allp = ResourcePackage.GetAllPackage();
            for(var i=allp.Count-1; i>=0;i--)
            {
                var res = allp[i].GetNamedResource<T>(name);
                if (res!=null)
                {
                    return res;
                }
            }
            return null;
        }
        public static IResource GetNamedResource(Type type, string name)
        {
            var allp = ResourcePackage.GetAllPackage();
            for (var i = allp.Count - 1; i >= 0; i--)
            {
                var res = allp[i].GetNamedResource(type,name);
                if (res != null)
                {
                    return res;
                }
            }
            return null;
        }
    }
    public class ResourcePackage
    {
        private ResourcePackage()
        {

        }
        static List<ResourcePackage> AllPackage = new List<ResourcePackage>();
        public static ResourcePackage Create()
        {
            var np = new ResourcePackage();
            AllPackage.Add(np);
            return np;
        }
        public static IReadOnlyList<ResourcePackage> GetAllPackage()
        {
            return AllPackage;
        }
        //0 号Package 是DefaultPackage
        public static void DestoryPackage(ResourcePackage package,bool force)
        {
            if (AllPackage[0] == package)
                throw new Exception("不能销毁默认包");
            package.RemoveAll(force);
            AllPackage.Remove(package);
        }
        //public void Apply()
        //{
        //    TTEngine.Runtime.activePackage = this;
        //}
        
        #region ID
        static uint _idcounter = 1;
        public static uint GenID()
        {
            return _idcounter++;
        }

        #endregion


        public void ClearUnused()
        {
            List<uint> unused = new List<uint>(); 
          
            foreach(var r in this.mapResources)
            {
                if (r.Value.CanDispose)
                {
                    r.Value.Destory();
                }
                if(r.Value.HasDispose)
                {
                    unused.Add(r.Key);
                }
            }
            for(var i=0; i<unused.Count; i++)
            {
                mapResources.Remove(unused[i]);
            }
            List<string> unusedname = new List<string>();
            foreach (var r in this.mapNamedRes)
            {
                unusedname.Clear();
                foreach (var n in r.Value)
                {
                    if (unused.Contains(n.Value))
                    {
                        unusedname.Add(n.Key);
                    }
                }
                foreach(var n in unusedname)
                {
                    r.Value.Remove(n);
                }
            }
        }


        #region 容器

        Dictionary<Type, NamedResourceMap> mapNamedRes = new Dictionary<Type, NamedResourceMap>();
        Dictionary<uint, IResource> mapResources = new Dictionary<uint, IResource>();
        public NamedResourceMap GetNamedResourceDict<T>(bool createnew = false)
        {
            if (mapNamedRes.TryGetValue(typeof(T), out var dict))
            {
                return dict;
            }
            if (createnew)
            {
                var map = mapNamedRes[typeof(T)] = new NamedResourceMap();
                return map;
            }
            return null;
        }
        public NamedResourceMap GetNamedResourceDict(Type key, bool createnew = false)
        {
            if (mapNamedRes.TryGetValue(key, out var dict))
            {
                return dict;
            }
            if (createnew)
            {
                var map = mapNamedRes[key] = new NamedResourceMap();
                return map;
            }
            return null;
        }

        public IReadOnlyDictionary<uint, IResource> GetResourceDict()
        {
            return mapResources;
        }
        #endregion
        #region 资源设置与获取
        public IResource GetResource(uint resid)
        {
            if (mapResources.TryGetValue(resid, out var res))
            {
                return res;
            }
            return null;
        }
        public T GetResource<T>(uint resid) where T : class, IResource
        {
            if (mapResources.TryGetValue(resid, out var res))
            {
                return res as T;
            }
            return null;
        }

        public T GetNamedResource<T>(string name) where T : class, IResource
        {
            var pool = GetNamedResourceDict<T>();
            if (pool == null)
                return null;
            if (pool.TryGetValue(name, out var resid))
            {
                return GetResource<T>(resid);
            }
            return null;
        }
        public IResource GetNamedResource(Type type, string name)
        {
            var pool = GetNamedResourceDict(type);
            if (pool == null)
                return null;
            if (pool.TryGetValue(name, out var resid))
            {
                return GetResource(resid);
            }
            return null;
        }

        
        public void AddResource(IResource resource)
        {
            mapResources.Add(resource.ResourceID, resource);
        }
        #endregion

        #region 设置uniqueName
        public void RemoveUniqueName(string name, IResource resource)
        {
            var pool = GetNamedResourceDict(resource.GetType());
            if (pool == null)
                return;
            pool.Remove(name);
        }
        public void SetUniqueName(string name, IResource resource)
        {
            var pool = GetNamedResourceDict(resource.GetType(), true);
            pool.Add(name, resource.ResourceID);
        }
        #endregion
        public void RemoveAll(bool force)
        {
            mapNamedRes.Clear();
            foreach (var res in mapResources)
            {
                if (res.Value.HasDispose)
                    continue;

                if (force)
                {
                    while (res.Value.UseCount > 0)
                        res.Value.DecRef();
                }
                else
                {
                    res.Value.DecRef();
                }
                if (res.Value.CanDispose)
                    res.Value.Destory();
            }
            mapResources.Clear();
        }
    }


}